﻿namespace Blaze.Framework.Pooling
{
    using System;
    using UnityEngine;

    /// <summary>
    /// <see cref="GameObject"/>对象池。
    /// </summary>
    public class LoopGameObjectPool : ObjectPool<GameObject>
    {
        /// <summary>
        /// 构造一个<see cref="ObjectPool&lt;T&gt;"/>的实例，并指定最大容量。
        /// </summary>
        /// <param name="prefab">用于创建游戏对象的预设</param>
        /// <param name="capacity">最大容量</param>
        public LoopGameObjectPool(GameObject prefab, int capacity)
            : base(capacity)
        {
            if (prefab == null)
                throw new ArgumentNullException("prefab");

            mPrefab = prefab;
        }

        /// <summary>
        /// 清理所有缓存的对象。
        /// </summary>
        public void Clear()
        {
            foreach (var obj in AvailiableObjects)
                UnityEngine.Object.Destroy(obj);
            foreach (var obj in InUseObjects)
                UnityEngine.Object.Destroy(obj);
            AvailiableObjects.Clear();
            InUseObjects.Clear();
        }

        /// <summary>
        /// 从对象池中获取一个对象。
        /// </summary>
        /// <returns>可用的对象</returns>
        public override GameObject Get()
        {
            GameObject ret;
            if (AvailiableObjects.Count != 0)
            {
                ret = AvailiableObjects.Pop();
            }
            else if (InUseObjects.Count == Capacity)
            {
                ret = InUseObjects[0];
                InUseObjects.RemoveAt(0);
                OnPut(ret);
            }
            else
            {
                ret = Create();
            }
            OnGet(ret);
            InUseObjects.Add(ret);
            return ret;
        }

        /// <summary>
        /// 将指定的对象回收到对象池。
        /// </summary>
        /// <param name="obj">对象</param>
        public override void Put(GameObject obj)
        {
            if (AvailiableObjects.Contains(obj))
                return;
            InUseObjects.Remove(obj);
            OnPut(obj);
            AvailiableObjects.Push(obj);
        }

        /// <summary>
        /// 回收所有正在使用的对象。
        /// </summary>
        public void RecycleAll()
        {
            while (InUseObjects.Count > 0)
            {
                var obj = InUseObjects[0];
                Put(obj);
            }
        }

        /// <summary>
        /// 当需要创建一个新的对象时调用此方法。
        /// </summary>
        /// <returns></returns>
        protected override GameObject Create()
        {
            return (GameObject) UnityEngine.Object.Instantiate(mPrefab);
        }

        /// <summary>
        /// 当需要从对象池中获取对象时调用此方法。
        /// </summary>
        /// <param name="obj"></param>
        protected override void OnGet(GameObject obj)
        {
            base.OnGet(obj);
            obj.SetActive(true);
        }

        /// <summary>
        /// 当需要回收指定对象时调用此方法。
        /// </summary>
        /// <param name="obj">需要回收的对象</param>
        protected override void OnPut(GameObject obj)
        {
            base.OnPut(obj);
            obj.SetActive(false);
        }

        private readonly GameObject mPrefab;
    }
}